package com.dream.auth.config;

import com.dream.auth.security.JwtAuthenticationFilter;
import com.dream.auth.security.UserDetailsServiceImpl;
import com.dream.auth.security.LoginSuccessHandler;
import com.dream.auth.security.CustomPasswordEncoder;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

	private final JwtAuthenticationFilter jwtAuthenticationFilter;

	private final UserDetailsServiceImpl userDetailsService;

	private final SecurityContextRepository securityContextRepository;

	private final LoginSuccessHandler loginSuccessHandler;

	private final CustomPasswordEncoder customPasswordEncoder;

	private static final String[] WHITELIST = {
			// 静态资源
			"/css/**", "/js/**", "/images/**", "/fonts/**", "/favicon.ico", "/assets/**",
			// Swagger/Knife4j
			"/doc.html", "/webjars/**", "/swagger-resources", "/swagger-resources/**", "/v2/api-docs",
			"/v2/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/configuration/ui",
			"/configuration/security", "/knife4j/",
			// 认证相关
			"/login", "/auth/login", "/auth/refresh-token",
			// OAuth2相关
			"/oauth2/**", "/authorize", "/token",
			// 错误页面
			"/error" };

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		return http.csrf()
				.disable()
				.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
				.and()
				.securityContext()
				.securityContextRepository(securityContextRepository)
				.and()
				.authorizeRequests()
				.antMatchers(WHITELIST)
				.permitAll()
				.antMatchers("/index")
				.authenticated()
				.anyRequest()
				.authenticated()
				.and()
				.formLogin()
				.loginPage("/login")
				.loginProcessingUrl("/login")
				.successHandler(loginSuccessHandler)
				.failureHandler((request, response, exception) -> {
					response.setContentType("application/json;charset=UTF-8");
					response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
					response.getWriter().write("{\"message\":\"用户名或密码错误\"}");
				})
				.and()
				.exceptionHandling()
				.authenticationEntryPoint((request, response, authException) -> {
					if (isAjaxRequest(request)) {
						response.setContentType("application/json;charset=UTF-8");
						response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
						response.getWriter().write("{\"message\":\"请先登录\"}");
					} else {
						response.sendRedirect("/login");
					}
				})
				.and()
				.authenticationProvider(authenticationProvider())
				.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
				.build();
	}

	private boolean isAjaxRequest(HttpServletRequest request) {
		String xRequestedWith = request.getHeader("X-Requested-With");
		return "XMLHttpRequest".equals(xRequestedWith);
	}

	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return web -> web.ignoring()
				.antMatchers(
						// 静态资源
						"/css/**", "/js/**", "/images/**", "/fonts/**", "/favicon.ico",
						// Swagger/Knife4j
						"/doc.html", "/webjars/**", "/swagger-resources", "/swagger-resources/**", "/doc.html",
						"/v2/api-docs", "/v2/api-docs/**",
						// Actuator端点
						"/actuator/**",
						// 其他
						"/error");
	}

	@Bean
	public AuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
		authProvider.setUserDetailsService(userDetailsService);
		authProvider.setPasswordEncoder(customPasswordEncoder);
		return authProvider;
	}

	@Bean
	public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
		return config.getAuthenticationManager();
	}
}